<?php

namespace Tictactoe\Ai;

use Tictactoe\Game as Game;
use Tictactoe\Tool as Tool;

class Estimate {

    const WINNER        = 1000000000;
    const WINNER_NEXT   = 10000000;
    const GOOD          = 100000;
    const NORMAL        = 1000;
    const BEGIN         = 10;
    const NOTHING       = 1;
    

    protected $limit = 5;
    protected $lastMove = array();

    /**
     * @var Game
     */
    protected $game = null;

    protected static $instance = null;
    
    /**
     * @param \Tictactoe\Game $game
     */
    public function __construct(Game $game) {
        $this->setGame($game);
    }

    /**
     * @param \Tictactoe\Game $game
     * @return \Tictactoe\Ai\Estimate
     */
    public static function getInstance(Game $game) {
        if (self::$instance === null ) {
            self::$instance = new self($game);
        }
        
        self::$instance->setGame($game);
        
        return self::$instance;
    }
    
    /**
     * @return Game
     */
    public function getGame() {
        return $this->game;
    }

    /**
     * @param Game $game
     */
    public function setGame(Game $game) {
        $this->game = $game;
    }

    /**
     * ocenia aktualny stan gry dla podanej wartosci
     * zwraca licze, im wieksza liczba tym lepszy rozklad
     * ruchow dla podanej wartosci v
     * 
     * @param Game $game
     * @param int $v
     * @return int
     */
    public function getEstimateForValue($v) {

        $points = 0;
        $size = $this->getGame()->getSize();


        /**
         * vertical
         */
        for ($x = 0; $x < $size; $x++) {

            $this->clearMove();

            for ($y = 0; $y < $size; $y++) {
                $value = $this->getGame()->getValue($x, $y);
                $this->addMove($x, $y, $value);
                $points += $this->calcPoints($v);
            }
        }


        /**
         * horyzontal
         */
        for ($y = 0; $y < $size; $y++) {

            $this->clearMove();

            for ($x = 0; $x < $size; $x++) {
                $value = $this->getGame()->getValue($x, $y);
                $this->addMove($x, $y, $value);
                $points += $this->calcPoints($v);
            }
        }

        /**
         * left to right cross JEST JUZ OK
         */
        for ($x = 0; $x < $size; $x++) {

            $this->clearMove();

            // petla LR_1
            // analizuje srodkowa przekatna
            for ($y = 0; $y < $size; $y++) {

                $xx = $y + $x;

                $value = $this->getGame()->getValue($xx, $y);
                if ($value === null) {
                    $this->clearMove();
                    continue;
                }
                $this->addMove($xx, $y, $value);
                $points += $this->calcPoints($v);
            }

            $this->clearMove();

            // petla LR_2
            for ($y = 0; $y < $size; $y++) {

                // warunek zeby dwa razy nie analizowac srodkowej przekatnej
                // ta srodkowa przekatna jest analizowana przez petle LR_1
                if ($x == 0) {
                    $this->clearMove();
                    continue;
                }

                $xx = $y - $x;

                $value = $this->getGame()->getValue($xx, $y);
                if ($value === null) {
                    $this->clearMove();
                    continue;
                }
                $this->addMove($xx, $y, $value);
                $points += $this->calcPoints($v);
            }
        }

        /**
         * right to left cross JEST JUZ OK
         */
        for ($x = $size - 1; $x >= 0; $x--) {

            $this->clearMove();

            // petla RL_1
            // analizuje srodkowa przekatna
            for ($y = 0; $y < $size; $y++) {

                $xx = $x - $y;

                $value = $this->getGame()->getValue($xx, $y);
                if ($value === null) {
                    $this->clearMove();
                    continue;
                }
                $this->addMove($xx, $y, $value);
                $points += $this->calcPoints($v);
            }

            $this->clearMove();

            // petla RL_2
            for ($y = 0; $y < $size; $y++) {

                // warunek zeby dwa razy nie analizowac srodkowej przekatnej
                // ta srodkowa przekatna jest analizowana przez petle RL_1
                if ($x == $size - 1) {
                    $this->clearMove();
                    continue;
                }

                // metoda prob i bledow
                $xx = $x - $y + 2 * ($size - 1 - $x);

                $value = $this->getGame()->getValue($xx, $y);
                if ($value === null) {
                    $this->clearMove();
                    continue;
                }
                $this->addMove($xx, $y, $value);
                $points += $this->calcPoints($v);
            }
        }

        return $points;
    }

    protected function addMove($x, $y, $valueXY) {
        $this->lastMove[] = array(
            'x' => $x,
            'y' => $y,
            'val' => $valueXY
        );
        $limit = $this->limit + 2; // 1 z przodu i 1 z tylu
        if (count($this->lastMove) > $limit) {
            $this->lastMove = array_slice($this->lastMove, 1);
        }
    }

    protected function calcPoints($value) {

        /**
         * jak w pieciu komorkach tablicy lastMoves 
         * jest jakis znak o wartosci value
         * a reszta znakow to wartosc 0
         * to zacznij punktowac
         * 100 = 5 x value
         * 80 = 4 x value
         * 50 = 3 x value
         * 30 = 2 x value
         * 10 = 1 x value
         */
        // pkt 1, czy sa tylko znaki value i znak 0
        
        $qty = count($this->lastMove);

        $points = 0;

        if ($qty >= $this->limit) {

            $values = Tool::arrayColumn($this->lastMove, 'val');

            /**
             * utworzenie tablic do analizy
             */
            $moves = array();
            if ($qty >= $this->limit) {
                $moves[] = array_slice($values, 0, $this->limit);
            }
            if ($qty >= $this->limit + 1) {
                $tmp = array_slice($values, 1, $this->limit);
                $tmp[] = $values[0];
                $moves[] = $tmp;
            }
            if ($qty >= $this->limit + 2) {
                $tmp = array_slice($values, 1, $this->limit);
                $tmp[] = $values[0];
                $tmp[] = $values[6];
                $moves[] = $tmp;

                $tmp = array_slice($values, 2, $this->limit);
                $tmp[] = $values[1];
                $moves[] = $tmp;
            }

            foreach ($moves as $move) {
                
                $calcAll = 0;
                $calcNextTo = 0;
        
                for ($i = 0; $i < $this->limit; $i++) {
                    $val = $move[$i];
                    if ($val == 0 || $val == $value) {
                        if ($val == $value) {
                            $calcAll++;
                            $calcNextTo++;
                        } else {
                            // jezeli nastepny ruch istnieje i nie jest ona wartoscia pusta [0 - zero]
                            if (isset($move[$i + 1]) && $move[$i + 1] != 0) {
                                $calcNextTo = 0;
                            }
                        }
                    } else {
                        $calcAll = 0;
                        $calcNextTo = 0;
                        break;
                    }
                }

                /**
                 * zlozenia
                 * 3 razy w 5 polach
                 * 
                 */
                if ($calcAll == 5) {
                    $points += self::WINNER;
                }

                if ($calcAll == 4) {
                    if ($move[0] == 0 && isset($move[6]) && $move[6] == 0) {
                        // uwaga sytuacja niebezpieczna
                        $points += self::WINNER_NEXT;
                    } else if ($move[4] == 0 && isset($move[5]) && $move[5] == 0) {
                        // uwaga sytuacja niebezpieczna
                        $points += self::WINNER_NEXT;
                    } else {
                         $points += self::GOOD;
                    }
                }

                if ($calcAll == 3) {
                    if ($calcNextTo == 3) {
                        if ( $move[0] == 0 || $move[1] == 0 || (isset($move[5]) && $move[5] == 0) &&
                             $move[3] == 0 || $move[4] == 0 || (isset($move[6]) && $move[6] == 0))
                        {
                            $points += self::GOOD;
                        }  else {
                            $points += self::GOOD / 2;
                        }
                    } else {
                        $points += self::BEGIN;
                    }
                }

                if ($calcAll == 2) {
                    $points += self::BEGIN;
                }

                if ($calcAll == 1) {
                    $points += self::NOTHING;
                }
            }
        }



        return $points;
    }

    protected function clearMove() {
        $this->lastMove = array();
    }

}
